package com.xy.xyaicpzs.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xy.xyaicpzs.common.ErrorCode;
import com.xy.xyaicpzs.constant.UserConstant;
import com.xy.xyaicpzs.domain.dto.user.UserPhoneLoginRequest;
import com.xy.xyaicpzs.domain.dto.user.UserPhoneRegisterRequest;
import com.xy.xyaicpzs.domain.entity.User;
import com.xy.xyaicpzs.domain.vo.*;
import com.xy.xyaicpzs.exception.BusinessException;
import com.xy.xyaicpzs.mapper.UserMapper;
import com.xy.xyaicpzs.service.SmsService;
import com.xy.xyaicpzs.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import jakarta.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;

/**
* @author XY003
* @description 针对表【user(用户表)】的数据库操作Service实现
* @createDate 2025-06-14 09:48:10
*/
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
    implements UserService{

    /**
     * 盐值，混淆密码
     */
    private static final String SALT = "xy";
    
    @Autowired
    private SmsService smsService;

    @Override
    public long userRegister(String userAccount, String userName, String userPassword, String checkPassword) {
        // 1. 校验
        if (StringUtils.isAnyBlank(userAccount, userName, userPassword, checkPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数为空");
        }
        if (userAccount.length() < 4) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户账号过短");
        }
        if (userName.length() > 40) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户名过长");
        }
        if (userPassword.length() < 8 || checkPassword.length() < 8) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户密码过短");
        }
        
        // 密码和校验密码相同
        if (!userPassword.equals(checkPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "两次输入的密码不一致");
        }
        
        synchronized (userAccount.intern()) {
            // 账户不能重复
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("userAccount", userAccount);
            long count = this.baseMapper.selectCount(queryWrapper);
            if (count > 0) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号重复");
            }
            
            // 2. 加密
            String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
            
            // 3. 插入数据
            User user = new User();
            user.setUserAccount(userAccount);
            user.setUserName(userName);
            user.setUserPassword(encryptPassword);
            user.setCreateTime(new Date());
            user.setUpdateTime(new Date());
            // 设置为VIP用户，有效期10天
            user.setIsVip(1);
            Date vipExpireDate = new Date(System.currentTimeMillis() + 10L * 24 * 60 * 60 * 1000);
            user.setVipExpire(vipExpireDate);
            boolean saveResult = this.save(user);
            if (!saveResult) {
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, "注册失败，数据库错误");
            }
            return user.getId();
        }
    }

    @Override
    public User userLogin(String userAccount, String userPassword, HttpServletRequest request) {
        // 1. 校验
        if (StringUtils.isAnyBlank(userAccount, userPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数为空");
        }
        if (userAccount.length() < 4) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号错误");
        }
        if (userPassword.length() < 8) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码错误");
        }
        
        // 2. 加密
        String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
        
        // 查询用户是否存在
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userAccount", userAccount);
        queryWrapper.eq("userPassword", encryptPassword);
        User user = this.baseMapper.selectOne(queryWrapper);
        
        // 用户不存在
        if (user == null) {
            log.info("user login failed, userAccount cannot match userPassword");
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户不存在或密码错误");
        }
        
        // 3. 记录用户的登录态
        request.getSession().setAttribute(UserConstant.USER_LOGIN_STATE, user);
        return getSafetyUser(user);
    }

    /**
     * 获取当前登录用户
     *
     * @param request
     * @return
     */
    @Override
    public User getLoginUser(HttpServletRequest request) {
        // 先判断是否已登录
        Object userObj = request.getSession().getAttribute(UserConstant.USER_LOGIN_STATE);
        User currentUser = (User) userObj;
        if (currentUser == null || currentUser.getId() == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
        }
        // 从数据库查询（追求性能的话可以注释，直接走缓存）
        long userId = currentUser.getId();
        currentUser = this.getById(userId);
        if (currentUser == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
        }
        return currentUser;
    }

    /**
     * 用户注销
     *
     * @param request
     */
    @Override
    public boolean userLogout(HttpServletRequest request) {
        if (request.getSession().getAttribute(UserConstant.USER_LOGIN_STATE) == null) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "未登录");
        }
        // 移除登录态
        request.getSession().removeAttribute(UserConstant.USER_LOGIN_STATE);
        return true;
    }

    @Override
    public User getSafetyUser(User originUser) {
        if (originUser == null) {
            return null;
        }
        User safetyUser = new User();
        safetyUser.setId(originUser.getId());
        safetyUser.setUserName(originUser.getUserName());
        safetyUser.setUserAccount(originUser.getUserAccount());
        safetyUser.setUserAvatar(originUser.getUserAvatar());
        safetyUser.setUserRole(originUser.getUserRole());
        safetyUser.setCreateTime(originUser.getCreateTime());
        safetyUser.setUpdateTime(originUser.getUpdateTime());
        return safetyUser;
    }

    @Override
    public boolean isAdmin(HttpServletRequest request) {
        // 仅管理员可查询
        Object userObj = request.getSession().getAttribute(UserConstant.USER_LOGIN_STATE);
        User user = (User) userObj;
        return isAdmin(user);
    }

    @Override
    public boolean isAdmin(User user) {
        return user != null && (UserConstant.ADMIN_ROLE.equals(user.getUserRole()) 
                || UserConstant.SUPER_ADMIN_ROLE.equals(user.getUserRole()));
    }
    
    @Override
    public long userPhoneRegister(UserPhoneRegisterRequest userPhoneRegisterRequest) {
        if (userPhoneRegisterRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "请求参数为空");
        }
        
        String userAccount = userPhoneRegisterRequest.getUserAccount();
        String userPassword = userPhoneRegisterRequest.getUserPassword();
        String checkPassword = userPhoneRegisterRequest.getCheckPassword();
        String phone = userPhoneRegisterRequest.getPhone();
        String code = userPhoneRegisterRequest.getCode();
        String userName = userPhoneRegisterRequest.getUserName();
        
        // 1. 校验
        if (StringUtils.isAnyBlank(userAccount, userPassword, checkPassword, phone, code)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数为空");
        }
        // 用户名可以为空，如果为空则默认使用账号
        if (StringUtils.isBlank(userName)) {
            userName = userAccount;
        }
        if (userAccount.length() < 4) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户账号过短");
        }
        if (userPassword.length() < 8 || checkPassword.length() < 8) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户密码过短");
        }
        
        // 密码和校验密码相同
        if (!userPassword.equals(checkPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "两次输入的密码不一致");
        }
        
        // 验证手机号格式
        String phoneRegex = "^1[3-9]\\d{9}$";
        if (!phone.matches(phoneRegex)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "手机号格式错误");
        }
        
        // 验证短信验证码
        boolean isCodeValid = smsService.verifyCode(phone, code);
        if (!isCodeValid) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "验证码错误或已过期");
        }
        
        synchronized (userAccount.intern()) {
            // 账户不能重复
            QueryWrapper<User> accountQueryWrapper = new QueryWrapper<>();
            accountQueryWrapper.eq("userAccount", userAccount);
            long accountCount = this.baseMapper.selectCount(accountQueryWrapper);
            if (accountCount > 0) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号已存在");
            }
            
            // 手机号不能重复
            QueryWrapper<User> phoneQueryWrapper = new QueryWrapper<>();
            phoneQueryWrapper.eq("phone", phone);
            long phoneCount = this.baseMapper.selectCount(phoneQueryWrapper);
            if (phoneCount > 0) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "手机号已注册");
            }
            
            // 2. 加密
            String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
            
            // 3. 插入数据
            User user = new User();
            user.setUserAccount(userAccount);
            user.setUserPassword(encryptPassword);
            user.setPhone(phone);
            // 设置用户名
            user.setUserName(userName);
            user.setCreateTime(new Date());
            user.setUpdateTime(new Date());
            // 设置为VIP用户，有效期10天
            user.setIsVip(0);
            Date vipExpireDate = new Date(System.currentTimeMillis() + 10L * 24 * 60 * 60 * 1000);
            user.setVipExpire(vipExpireDate);
            
            boolean saveResult = this.save(user);
            if (!saveResult) {
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, "注册失败，数据库错误");
            }
            return user.getId();
        }
    }
    
    @Override
    public User userPhoneLogin(UserPhoneLoginRequest userPhoneLoginRequest, HttpServletRequest request) {
        if (userPhoneLoginRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "请求参数为空");
        }
        
        String phone = userPhoneLoginRequest.getPhone();
        String code = userPhoneLoginRequest.getCode();
        
        // 1. 校验参数
        if (StringUtils.isAnyBlank(phone, code)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数为空");
        }
        
        // 验证手机号格式
        String phoneRegex = "^1[3-9]\\d{9}$";
        if (!phone.matches(phoneRegex)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "手机号格式错误");
        }
        
        // 验证短信验证码
        boolean isCodeValid = smsService.verifyCode(phone, code);
        if (!isCodeValid) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "验证码错误或已过期");
        }
        
        // 查询用户是否存在
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("phone", phone);
        User user = this.baseMapper.selectOne(queryWrapper);
        
        // 用户不存在
        if (user == null) {
            log.info("user login failed, phone number not registered");
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "手机号未注册");
        }
        
        // 3. 记录用户的登录态
        request.getSession().setAttribute(UserConstant.USER_LOGIN_STATE, user);
        return getSafetyUser(user);
    }
    
    @Override
    public String encryptPassword(String password) {
        if (StringUtils.isBlank(password)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码不能为空");
        }
        return DigestUtils.md5DigestAsHex((SALT + password).getBytes());
    }
    
    // region 统计相关方法实现
    
    @Override
    public UserStatisticsVO getNewUsersStatistics(String startDate, String endDate) {
        try {
            // 构建查询条件
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.between("createTime", startDate + " 00:00:00", endDate + " 23:59:59");
            
            // 获取新增用户总数
            long totalNewUsers = this.count(queryWrapper);
            
            // 获取新增用户列表（最近20个）
            queryWrapper.orderByDesc("createTime");
            queryWrapper.last("LIMIT 20");
            List<User> recentUsers = this.list(queryWrapper);
            List<UserVO> recentUserVOs = recentUsers.stream().map(user -> {
                UserVO userVO = new UserVO();
                BeanUtils.copyProperties(user, userVO);
                return userVO;
            }).collect(Collectors.toList());
            
            return UserStatisticsVO.builder()
                    .totalNewUsers(totalNewUsers)
                    .startDate(startDate)
                    .endDate(endDate)
                    .recentUsers(recentUserVOs)
                    .build();
                    
        } catch (Exception e) {
            log.error("获取新增用户统计失败：{}", e.getMessage());
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "获取统计数据失败");
        }
    }
    
    @Override
    public VipStatisticsVO getNewVipsStatistics(String startDate, String endDate) {
        try {
            // 构建查询条件 - 新增会员（isVip != 0 且在时间范围内）
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.ne("isVip", 0);
            queryWrapper.between("createTime", startDate + " 00:00:00", endDate + " 23:59:59");
            
            // 获取新增会员总数
            long totalNewVips = this.count(queryWrapper);
            
            // 获取新增会员列表（最近20个）
            queryWrapper.orderByDesc("createTime");
            queryWrapper.last("LIMIT 20");
            List<User> recentVips = this.list(queryWrapper);
            List<UserVO> recentVipVOs = recentVips.stream().map(user -> {
                UserVO userVO = new UserVO();
                BeanUtils.copyProperties(user, userVO);
                return userVO;
            }).collect(Collectors.toList());
            
            // 计算会员转化率（该时间段内新增会员 / 新增用户总数）
            QueryWrapper<User> allUsersWrapper = new QueryWrapper<>();
            allUsersWrapper.between("createTime", startDate + " 00:00:00", endDate + " 23:59:59");
            long totalNewUsers = this.count(allUsersWrapper);
            
            double conversionRate = totalNewUsers > 0 ? (double) totalNewVips / totalNewUsers * 100 : 0.0;
            
            return VipStatisticsVO.builder()
                    .totalNewVips(totalNewVips)
                    .totalNewUsers(totalNewUsers)
                    .conversionRate(Math.round(conversionRate * 100.0) / 100.0)
                    .startDate(startDate)
                    .endDate(endDate)
                    .recentVips(recentVipVOs)
                    .build();
                    
        } catch (Exception e) {
            log.error("获取新增会员统计失败：{}", e.getMessage());
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "获取统计数据失败");
        }
    }
    
    @Override
    public RegistrationTrendVO getRegistrationTrend(String startDate, String endDate, String granularity) {
        try {
            // 这里简化处理，实际应该使用SQL的GROUP BY和DATE_FORMAT函数
            // 由于MyBatis-Plus的限制，我们先获取所有数据然后在Java中分组
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.between("createTime", startDate + " 00:00:00", endDate + " 23:59:59");
            queryWrapper.orderByAsc("createTime");
            List<User> users = this.list(queryWrapper);
            
            Map<String, Long> trendData = new HashMap<>();
            Map<String, Long> vipTrendData = new HashMap<>();
            
            // 在Java中按指定粒度分组统计
            for (User user : users) {
                String key = formatDateByGranularity(user.getCreateTime(), granularity);
                trendData.put(key, trendData.getOrDefault(key, 0L) + 1);
                
                // 同时统计会员趋势
                if (user.getIsVip() != null && user.getIsVip() != 0) {
                    vipTrendData.put(key, vipTrendData.getOrDefault(key, 0L) + 1);
                }
            }
            
            return RegistrationTrendVO.builder()
                    .startDate(startDate)
                    .endDate(endDate)
                    .granularity(granularity)
                    .userTrend(trendData)
                    .vipTrend(vipTrendData)
                    .totalUsers(users.size())
                    .totalVips(users.stream().mapToLong(u -> u.getIsVip() != null && u.getIsVip() != 0 ? 1 : 0).sum())
                    .build();
                    
        } catch (Exception e) {
            log.error("获取用户注册趋势失败：{}", e.getMessage());
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "获取趋势数据失败");
        }
    }
    
    @Override
    public Page<UserVO> getExpiringVips(Integer days, Long current, Long pageSize) {
        try {
            // 计算目标日期范围
            Date now = new Date();
            Date futureDate = new Date(now.getTime() + days * 24 * 60 * 60 * 1000L);
            
            // 构建查询条件
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.ne("isVip", 0); // 是会员
            queryWrapper.isNotNull("vipExpire"); // 有到期时间
            queryWrapper.between("vipExpire", now, futureDate); // 在即将到期的时间范围内
            queryWrapper.orderByAsc("vipExpire"); // 按到期时间升序
            
            Page<User> userPage = this.page(new Page<>(current, pageSize), queryWrapper);
            
            // 转换为UserVO
            Page<UserVO> userVOPage = new Page<>(userPage.getCurrent(), userPage.getSize(), userPage.getTotal());
            List<UserVO> userVOList = userPage.getRecords().stream().map(user -> {
                UserVO userVO = new UserVO();
                BeanUtils.copyProperties(user, userVO);
                return userVO;
            }).collect(Collectors.toList());
            userVOPage.setRecords(userVOList);
            
            return userVOPage;
            
        } catch (Exception e) {
            log.error("获取即将到期会员失败：{}", e.getMessage());
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "获取数据失败");
        }
    }
    
    @Override
    public VipDistributionVO getVipDistribution() {
        try {
            Date now = new Date();
            
            // 总用户数
            long totalUsers = this.count();
            
            // 普通用户数（isVip = 0 或 null）
            QueryWrapper<User> normalWrapper = new QueryWrapper<>();
            normalWrapper.and(wrapper -> wrapper.eq("isVip", 0).or().isNull("isVip"));
            long normalUsers = this.count(normalWrapper);
            
            // 有效会员数（isVip != 0 且 vipExpire > now）
            QueryWrapper<User> activeVipWrapper = new QueryWrapper<>();
            activeVipWrapper.ne("isVip", 0)
                    .and(wrapper -> wrapper.isNull("vipExpire").or().gt("vipExpire", now));
            long activeVips = this.count(activeVipWrapper);
            
            // 过期会员数（isVip != 0 且 vipExpire <= now）
            QueryWrapper<User> expiredVipWrapper = new QueryWrapper<>();
            expiredVipWrapper.ne("isVip", 0)
                    .isNotNull("vipExpire")
                    .le("vipExpire", now);
            long expiredVips = this.count(expiredVipWrapper);
            
            // 即将到期会员数（7天内到期）
            Date sevenDaysLater = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000L);
            QueryWrapper<User> soonExpireWrapper = new QueryWrapper<>();
            soonExpireWrapper.ne("isVip", 0)
                    .between("vipExpire", now, sevenDaysLater);
            long soonExpireVips = this.count(soonExpireWrapper);
            
            // 计算百分比
            double normalPercentage = totalUsers > 0 ? (double) normalUsers / totalUsers * 100 : 0;
            double activeVipPercentage = totalUsers > 0 ? (double) activeVips / totalUsers * 100 : 0;
            double expiredVipPercentage = totalUsers > 0 ? (double) expiredVips / totalUsers * 100 : 0;
            
            return VipDistributionVO.builder()
                    .totalUsers(totalUsers)
                    .normalUsers(normalUsers)
                    .normalPercentage(Math.round(normalPercentage * 100.0) / 100.0)
                    .activeVips(activeVips)
                    .activeVipPercentage(Math.round(activeVipPercentage * 100.0) / 100.0)
                    .expiredVips(expiredVips)
                    .expiredVipPercentage(Math.round(expiredVipPercentage * 100.0) / 100.0)
                    .soonExpireVips(soonExpireVips)
                    .statisticsTime(now)
                    .build();
                    
        } catch (Exception e) {
            log.error("获取会员分布统计失败：{}", e.getMessage());
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "获取统计数据失败");
        }
    }
    
    /**
     * 根据时间粒度格式化日期
     */
    private String formatDateByGranularity(Date date, String granularity) {
        if (date == null) return "";
        
        java.time.LocalDateTime localDateTime = date.toInstant().atZone(java.time.ZoneId.systemDefault()).toLocalDateTime();
        java.time.format.DateTimeFormatter formatter;
        
        switch (granularity) {
            case "week":
                // 获取年份和周数
                int year = localDateTime.getYear();
                int week = localDateTime.get(java.time.temporal.WeekFields.of(java.util.Locale.getDefault()).weekOfYear());
                return year + "-W" + String.format("%02d", week);
            case "month":
                formatter = java.time.format.DateTimeFormatter.ofPattern("yyyy-MM");
                break;
            default:
                formatter = java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd");
                break;
        }
        
        return localDateTime.format(formatter);
    }
    
    // endregion
} 